package com.cash.loan.common.util;

import com.cash.loan.common.exception.RedisException;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import redis.clients.util.SafeEncoder;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author Administrator
 * @Date 2018-07-05 11:04
 */
@Component
public class RedisUtil<T> {

    private static final Logger logger = Logger.getLogger(RedisUtil.class);

    @Autowired
    private RedisTemplate<String,T> redisTemplate;

    public void setRedisTemplate(RedisTemplate<String, T> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public static final String KEY_SET_PREFIX = "_set:";

    private static final String KEY_LIST_PREFIX = "_list:";

    /**
     * 通过key获得value
     * @param key
     * @return
     */
    public T get(String key) {
        logger.debug("get key [{"+key+"}]");
        try{
            if(key == null) {
                return null;
            } else {
                return redisTemplate.opsForValue().get(key);
            }
        }catch (Throwable t){
            logger.error(t+":get key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 写入key-value键值对
     * @param key
     * @param value
     * @return
     */
    public T set(String key,T value) {
        logger.debug("set key [{"+key+"}]");
        try{
            redisTemplate.opsForValue().set(key,value);
            return value;
        }catch (Throwable t) {
            logger.error(t+":set key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 写入key-value并设置过期时间
     * @param key
     * @param value
     * @param expireTime
     * @return
     */
    public T set(String key,T value,long expireTime) {
        logger.debug("set key [{"+key+"}]");
        try{
            redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.MINUTES);
            return value;
        }catch(Throwable t){
            logger.error(t+":set key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 写入key-value键值对，同时设置ex和nx
     * @param key
     * @param value
     * @param nxxx
     * @param expx
     * @param expireTime
     * @return
     */
    public boolean set(final String key,final String value, final String nxxx,final String expx,final long expireTime) {
        Object result = redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                connection.execute("set", new byte[][]{
                        serializer.serialize(key), serializer.serialize(value),
                        serializer.serialize(nxxx), serializer.serialize(expx),
                        SafeEncoder.encode(String.valueOf(expireTime))

                });
                return true;
            }
        });
        return false;
    }

    /**
     * 根据key删除对应数据
     * @param key
     */
    public void delete(String key) {
        logger.debug("delete key [{"+key+"}]");
        try {
            redisTemplate.delete(key);
        }
        catch (Throwable t) {
            logger.error(t+":delete key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 删除所有的key
     * @param keys
     */
    public void deleteKeys(Collection<String> keys) {
        logger.debug("delete key [{"+keys+"}]");
        try {
            redisTemplate.delete(keys);
        }
        catch (Throwable t) {
            logger.error(t+":delete key [{"+keys+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 更新key对应数据的过期时间
     * @param key
     * @param expireTime
     */
    public void updateExpireTime(String key,long expireTime) {
        logger.debug("update expireTime for [{"+key+"}]");
        try{
            redisTemplate.expire(key,expireTime,TimeUnit.MINUTES);
        }catch (Throwable t) {
            logger.error(t+"update expireTime for [{"+key+"}]");
            throw new RedisException(t);
        }
    }

    /**
     * 保存set类型的key-value,并设置过期
     * @param k
     * @param value
     * @param time
     */
    @SuppressWarnings("unchecked")
    public void setSet(String k, T value, long time) {
        String key = KEY_SET_PREFIX + k;
        logger.debug("setSet key [{"+key+"}]");
        try {
            SetOperations<String, T> valueOps = redisTemplate.opsForSet();
            valueOps.add(key, value);
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }
        catch (Throwable t) {
            logger.error(t+"setSet key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 保存set类型的key-value不设置过期
     * @param k
     * @param value
     */
    public void setSet(String k, T value) {
        setSet(k, value, -1);
    }

    /**
     * 保存set类型key-value,其中value为set集合，设置过期
     * @param k
     * @param v
     * @param time
     */
    @SuppressWarnings("unchecked")
    public void setSet(String k, Set<T> v, long time) {
        String key = KEY_SET_PREFIX + k;
        logger.debug("setSet key [{"+key+"}]");
        try {
            SetOperations<String, T> setOps = redisTemplate.opsForSet();
            setOps.add(key, (T[]) v.toArray());
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }
        catch (Throwable t) {
            logger.error(t+":setSet key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    public void setSet(String k, Set<T> v) {
        setSet(k, v, -1);
    }

    /**
     * 获得set类型数据
     * @param k
     * @return
     */
    public Set<T> getSet(String k) {
        String key = KEY_SET_PREFIX + k;
        logger.debug("getSet key [{"+key+"}]");
        try {
            SetOperations<String, T> setOps = redisTemplate.opsForSet();
            return setOps.members(key);
        }
        catch (Throwable t) {
            logger.error(t+"getSet key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 移除set中的数据
     * @param k
     * @param value
     * @return
     */
    public Long removeSet(String k, T value) {
        String key = KEY_SET_PREFIX + k;
        logger.debug("getSet key [{"+key+"}]");
        try {
            SetOperations<String, T> setOps = redisTemplate.opsForSet();
            return setOps.remove(key,value);
        } catch (Throwable t) {
            logger.error(t+"getSet key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 得到set的长度
     * @param k
     * @return
     */
    public Long getSetSize(String k) {
        String key = KEY_SET_PREFIX + k;
        logger.debug("getSet key [{"+key+"}]");
        try {
            SetOperations<String, T> setOps = redisTemplate.opsForSet();
            return setOps.size(key);
        } catch (Throwable t) {
            logger.error(t+"getSet key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 设置list类型的数据
     * @param k
     * @param v
     * @param time
     */
    public void setList(String k, T v, long time) {
        String key = KEY_LIST_PREFIX + k;
        logger.debug("setList key [{"+key+"}]");
        try {
            ListOperations<String, T> listOps = redisTemplate.opsForList();
            listOps.rightPush(key, v);
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }
        catch (Throwable t) {
            logger.error(t+":setList key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    public void setList(String k, T v) {
        setList(k,v,-1);
    }

    /**
     * 设置list集合类型数据
     * @param k
     * @param v
     * @param time
     */
    public void setList(String k, List<T> v, long time) {
        String key = KEY_LIST_PREFIX + k;
        logger.debug("setList key [{"+key+"}]");
        try {
            ListOperations<String, T> listOps = redisTemplate.opsForList();
            listOps.rightPushAll(key, v);
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }
        catch (Throwable t) {
            logger.error(t+":setList key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    public void setList(String k, List<T> v) {
        setList(k, v, -1);
    }

    /**
     * 获得list类型数据
     * @param k
     * @param start
     * @param end
     * @return
     */
    public List<T> getList(String k, long start, long end) {
        String key = KEY_LIST_PREFIX + k;
        logger.debug("setList key [{"+key+"}]");
        try {
            ListOperations<String, T> listOps = redisTemplate.opsForList();
            return listOps.range(key, start, end);
        }
        catch (Throwable t) {
            logger.error(t+":getList key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 获得list数据类型的长度
     * @param k
     * @return
     */
    public long getListSize(String k) {
        String key = KEY_LIST_PREFIX + k;
        logger.debug("setList key [{"+key+"}]");
        try {
            ListOperations<String, T> listOps = redisTemplate.opsForList();
            return listOps.size(key);
        }
        catch (Throwable t) {
            logger.error(t+":getListSize key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 获得list数据类型集合的长度
     * @param listOps
     * @param k
     * @return
     */
    public long getListSize(ListOperations<String, String> listOps, String k) {
        String key = KEY_LIST_PREFIX + k;
        logger.debug("getListSize key [{"+key+"}]");
        try {
            return listOps.size(key);
        }
        catch (Throwable t) {
            logger.error(t+":getListSize key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    public Long removeListValue(String k, T value) {
        String key = KEY_LIST_PREFIX + k;
        logger.debug("getList key [{"+key+"}]");
        try {
            ListOperations<String, T> listOps = redisTemplate.opsForList();
            return listOps.remove(key,0,value);
        } catch (Throwable t) {
            logger.error(t+"getList key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    public String removeListLeftValue(String k) {
        String key = KEY_LIST_PREFIX + k;
        logger.debug("getList key [{"+key+"}]");
        try {
            ListOperations<String, T> listOps = redisTemplate.opsForList();
            return (String) listOps.leftPop(key);
        } catch (Throwable t) {
            logger.error(t+"getList key [{"+key+"}] exception!");
            throw new RedisException(t);
        }
    }

    /**
     * 设置hash数据类型的数据
     * @param key
     * @param mapkey
     * @param mapValue
     */
    public void setMap(String key, String mapkey, T mapValue) {
        HashOperations<String, String, T> hashOperations = redisTemplate.opsForHash();
        hashOperations.putIfAbsent(key, mapkey, mapValue);
    }

    /**
     * 递增mapKey的值
     * @param key
     * @param mapKey
     */
    public void incrMap(String key,String mapKey) {
        HashOperations<String, String, T> hashOperations = redisTemplate.opsForHash();
        hashOperations.increment(key,mapKey,1L);
    }

    /**
     * 删除hash数据
     * @param key
     * @param mapkey
     */
    public void deleteMap(String key, String mapkey) {
        HashOperations<String, String, T> hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, mapkey);
    }

    /**
     * 获取hash数据
     * @param key
     * @param mapkey
     * @return
     */
    public T getMap(String key, String mapkey) {
        HashOperations<String, String, T> hashOperations = redisTemplate.opsForHash();
        return hashOperations.get(key, mapkey);
    }

    /**
     * 判断hashKey是否存在
     * @param key
     * @param hashKey
     * @return
     */
    public Boolean hasHashKey(String key,Object hashKey) {
        HashOperations<String, String, T> hashOperations = redisTemplate.opsForHash();
        return hashOperations.hasKey(key,hashKey);
    }

    /**
     * 增加hashValue的值
     * @param key
     * @param hashKey
     * @param value
     * @return
     */
    public Long incrementHashValue(String key,Object hashKey,Long value) {
        HashOperations<String, String, T> hashOperations = redisTemplate.opsForHash();
        return hashOperations.increment(key, String.valueOf(hashKey),value);
    }

    /**
     * 获取hash数据中的所有value
     * @param key
     * @return
     */
    public List<T> getMapValues(String key) {
        HashOperations<String, String, T> hashOperations = redisTemplate.opsForHash();
        return hashOperations.values(key);
    }

    public Boolean existKey(String key) {
        return redisTemplate.hasKey(key);
    }

    public void incr(String key,long num) {
        redisTemplate.opsForValue().increment(key,num);
    }

    public Long getExpireTime(String token) {
        return redisTemplate.getExpire(token);
    }

    public Set<String> getKeyByPattern(String s) {
        return redisTemplate.keys(s);
    }

    /**
     * 重命名
     * @param oldKey
     * @param newKey
     */
    public void rename(String oldKey,String newKey) {
        redisTemplate.rename(oldKey,newKey);
    }
}
